(function() {
	var oncontextmenu = document.oncontextmenu;
	var LBUTTON = 0, MBUTTON = 1, RBUTTON = 2;
	var LEFT = 'L', RIGHT = 'R', UP = 'U', DOWN = 'D';
	var path, endtm, lastX, lastY, accuX, accuY;
	var lean = 1 / Math.tan(30 * Math.PI / 180);	// lean tolerance is 60 degrees
	var in_config = (chrome.extension.getURL('options.html') === location.href);
	var trail_canvas, trail_ctx;
	var port = chrome.extension.connect({name: 'stroke.html-stroke'});
	/* default config */
	var cfg = {
		trigger: RBUTTON,
		timeout: 200,
		minstep: 20,
		dotrail: true
	}

	/* calculate weight by accumulated distances moved in both X and Y */
	var weigh = function(x, y) {
		return Math.round(Math.sqrt(x * x + y * y));
	}

	document.addEventListener('mousedown', function(ev) {
		if (ev.button != cfg.trigger) return;

		/* restore right click handler */
		if (cfg.trigger == RBUTTON) {
			document.oncontextmenu = oncontextmenu;
		}

		if (cfg.dotrail) {
			/* create the canvas element */
			if (!trail_ctx) {
				trail_canvas = document.createElement('canvas');
				trail_canvas.style.cssText = 'z-index:1; position:fixed; top:0; left:0;';
				trail_canvas.width = window.innerWidth;
				trail_canvas.height = window.innerHeight;
				document.body.appendChild(trail_canvas);
				trail_ctx = trail_canvas.getContext('2d');
				/* styling */
				trail_ctx.globalAlpha = 0.3;
				trail_ctx.lineWidth = 3;
				trail_ctx.lineCap = 'round';
				trail_ctx.shadowOffsetX = 0;
				trail_ctx.shadowOffsetY = 0;
				trail_ctx.shadowBlur = 3;
				trail_ctx.shadowColor = 'grey';
				trail_ctx.strokeStyle = 'black';
			} else {
				trail_canvas.style.display = '';
			}
			trail_ctx.beginPath();
			trail_ctx.moveTo(ev.clientX, ev.clientY);
		}

		/* start recording, reset everything */
		path = {count: 0, strokes: [], weights: []};
		lastX = ev.screenX, lastY = ev.screenY;
		accuX = accuY = 0;
	}, false);

	document.addEventListener('mousemove', function(ev) {
		if (ev.button != cfg.trigger || !path) return;

		var stroke, x = ev.screenX, y = ev.screenY,
			offsetX = x - lastX, offsetY = y - lastY,
			absX = Math.abs(offsetX), absY = Math.abs(offsetY);

		if (absX < cfg.minstep && absY < cfg.minstep) return;
		/* draw trail if enabled */
		if (cfg.dotrail) {
			trail_ctx.lineTo(ev.clientX, ev.clientY);
			trail_ctx.stroke();
		}
		lastX = x, lastY = y;
		accuX += absX, accuY += absY;

		/* ignore leaning, blurrer strokes are more recognizable */
//		if (0 == absY || (absX / absY > lean)) {
		if (absX > absY) {
			stroke = offsetX > 0 ? RIGHT : LEFT;
//		} else if (0 == absX || (absY / absX > lean)) {
		} else {
			stroke = offsetY > 0 ? DOWN : UP;
//		} else {
//			/* leaned strokes are always start with horizontal direction */
//			stroke = (offsetX > 0 ? RIGHT : LEFT) + (offsetY > 0 ? DOWN : UP);
		}
		if (0 == path.count || path.strokes[path.count - 1] != stroke) {
			accuX = absX, accuY = absY;
			++path.count;
			path.strokes.push(stroke);
			path.weights.push(weigh(accuX, accuY));
		} else {
			/* update weight */
			path.weights[path.count - 1] = weigh(accuX, accuY);
		}
		endtm = new Date().getTime();	// when mouse stops
	}, false);

	document.addEventListener('mouseup', function(ev) {
		if (ev.button != cfg.trigger) return;

		/* disable right click if the trigger button is right button */
		if (cfg.trigger == RBUTTON) {
			document.oncontextmenu = function() { return false };
		}
		if (cfg.dotrail) {
			trail_canvas.style.display = 'none';
			trail_ctx.clearRect(0, 0, trail_canvas.width, trail_canvas.height);
		}

		if (!path || !path.count || (new Date().getTime() - endtm) > cfg.timeout)
			return;

		if (in_config) {
			/* post event for options.html */
			var e = document.createEvent('Event');
			e.initEvent('set-stroke-event', false, false);
			e.strokes = path.strokes;
			document.dispatchEvent(e);
		} else {
			/* post regular strokes */
			port.postMessage(path.strokes);
		}
	}, true);

	/* response to background page's request */
	chrome.extension.onRequest.addListener(function(req) {
		if ('update-config' == req.req) {
			cfg = req.arg;
		} else if ('local-eval' == req.req) {
			try {
				eval(req.arg);
			} catch (ex) {
				console.error(ex);
			}
		}
	});
})();
